package com.huayu.languo.common.configurer.myplus;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.core.parser.ISqlParser;
import com.baomidou.mybatisplus.core.parser.ISqlParserFilter;
import com.baomidou.mybatisplus.core.parser.SqlParserHelper;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.incrementer.H2KeyGenerator;
import com.baomidou.mybatisplus.extension.injector.methods.additional.InsertBatchSomeColumn;
import com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor;
import com.baomidou.mybatisplus.extension.plugins.SqlExplainInterceptor;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;
import com.huayu.languo.model.anno.Dev;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

@Configuration
@MapperScan("com.huayu.languo.mapper*")
public class MybatisPlusConfig {

    /**    @Bean  启动  会自动在where条件下加上 .tenant_id = 1
     * mybatis-plus分页插件
     * @Date: 2019/5/18 23:34
     * @author: lxk
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        List<ISqlParser> sqlParserList = new ArrayList<>();


        /*
         * 【测试多租户】 SQL 解析处理拦截器<br>
         * 这里固定写成住户 1 实际情况你可以从cookie读取，因此数据看不到 【 麻花藤 】 这条记录（ 注意观察 SQL ）<br>
         */
        TenantSqlParser tenantSqlParser = new TenantSqlParser();
        tenantSqlParser.setTenantHandler(new TenantHandler() {

            @Override
            public Expression getTenantId() {
                return new LongValue(1L);
            }

            @Override
            public String getTenantIdColumn() {
                return "tenant_id";
            }

            @Override
            public boolean doTableFilter(String tableName) {
                // 这里可以判断是否过滤表
                if ("user".equals(tableName)) {
                    return true;
                }
                return false;
            }
        });

        sqlParserList.add(tenantSqlParser);
        paginationInterceptor.setSqlParserList(sqlParserList);
        paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {

            @Override
            public boolean doFilter(MetaObject metaObject) {
//                MappedStatement ms = PluginUtils.getMappedStatement(metaObject);
                MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);
                // 过滤自定义查询, 此时无租户信息约束【 麻花藤 】出现
                if ("com.baomidou.springboot.mapper.UserMapper.selectListBySQL".equals(ms.getId())) {
                    return true;
                }
                //return false;
                return true;
            }
        });


        // 攻击 SQL 阻断解析器、加入解析链
        sqlParserList.add(new BlockAttackSqlParser());


        return paginationInterceptor;
    }


    /**
     * TODO 没效果？
     * 这个插件配置了一个属性，stopProceed设置为true后，如果执行的是删除表中全部内容，那就会抛出异常，终止该操作。该插件主要是防止手抖误删数据。
     */
    @Bean
// @Dev
    public SqlExplainInterceptor sqlExplainInterceptor() {
        SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
        sqlExplainInterceptor.setSqlParserList(CollUtil.newArrayList(new BlockAttackSqlParser()));
        Properties properties = new Properties();
        properties.put("stopProceed", true);
        sqlExplainInterceptor.setProperties(properties);
        return sqlExplainInterceptor;
    }


    /**
     * 乐观锁插件
     */
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }


    /**
     * 相当于class上的： @MapperScan("com.huayu.languo.mapper*")
     *  使用 MapperScannerConfigurer 会有很多 No MyBatis mapper was found in 的警告
     */
    //@Bean
    //public MapperScannerConfigurer mapperScannerConfigurer() {
    //    MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer();
    //    scannerConfigurer.setBasePackage("com.huayu.languo.mapper*");
    //    return scannerConfigurer;
    //}

    @Bean
    public H2KeyGenerator getH2KeyGenerator() {
        return new H2KeyGenerator();
    }


    /**
     * 性能分析拦截器，不建议生产使用
     */
    @Bean
    @Dev
    public PerformanceInterceptor performanceInterceptor() {
        //格式化SQL语句
        //sql执行时间超过 1000L 就会停止执行
        return new PerformanceInterceptor().setFormat(true).setMaxTime(1000L);
    }




    /**
     *  自定义sql注入
     */
    @Bean
    public ISqlInjector sqlInjector() {
        return new DefaultSqlInjector() {

            @Override
            public List<AbstractMethod> getMethodList() {
                List<AbstractMethod> methodList = super.getMethodList();
                methodList.add(new DeleteAll());
                methodList.add(new InsertBatchSomeColumn());
                return methodList;
            }
        };
    }

}
